package io.github.bluesbruce.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 文件上传，Netty服务端
 * <p>需要禁用Nagle算法。Nagle算法：试图减少TCP包的数量和结构性开销，
 * 将多个较小的包组合成较大的包进行发送。受TCP延迟确认影响， 会导致相继两次向连接发送请求包，读数据时会有一个最多达500毫秒的延时。</p>
 *
 * @author BBF
 */
public class DolphinServer {

  private static final Logger LOGGER = LoggerFactory.getLogger(DolphinServer.class);

  public void bind(int port) throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
          //实例化ServerSocketChannel
          .channel(NioServerSocketChannel.class)
          //设置ServerSocketChannel的TCP参数
          .option(ChannelOption.SO_BACKLOG, 1024)
          // 禁用Nagle算法，降低网络延迟
          .childOption(ChannelOption.TCP_NODELAY, true)
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
              ch.pipeline().addLast(new ObjectEncoder());
              ch.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE,
                  ClassResolvers.weakCachingConcurrentResolver(null)));
              ch.pipeline().addLast(new DolphinServerHandler());
            }
          });
      //绑定监听端口，调用sync同步阻塞方法等待绑定操作完
      ChannelFuture future = b.bind(port).sync();
      LOGGER.info("Netty服务启动，监听端口 = {}", port);
      //成功绑定到端口之后,给channel增加一个管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
      future.channel().closeFuture().sync();
    } finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }
  }
}
